package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ListServiceImpl implements ListService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private GoodsDao goodsDao;

    @Resource
    private ProductFeignClient productFeignClient;

    @Resource
    private RedisTemplate redisTemplate;

    //商品上架 添加索引
    @Override
    public void upperGoods(Long skuId) {
        Goods goods = new Goods();
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        BaseTrademark baseTrademark = productFeignClient.getBaseTrademark(skuInfo.getTmId());
        BaseCategoryView baseCategoryView = productFeignClient.getBaseCategoryView(skuInfo.getCategory3Id());
        List<SkuAttrValue> skuAttrValueList = productFeignClient.getSkuAttrValue(skuId);
        goods.setId(skuId);
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        goods.setTitle(skuInfo.getSkuName());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setCreateTime(new Date());
        goods.setTmId(baseTrademark.getId());
        goods.setTmName(baseTrademark.getTmName());
        goods.setTmLogoUrl(baseTrademark.getLogoUrl());
        goods.setCategory1Id(baseCategoryView.getCategory1Id());
        goods.setCategory1Name(baseCategoryView.getCategory1Name());
        goods.setCategory2Id(baseCategoryView.getCategory2Id());
        goods.setCategory2Name(baseCategoryView.getCategory2Name());
        goods.setCategory3Id(baseCategoryView.getCategory3Id());
        goods.setCategory3Name(baseCategoryView.getCategory3Name());
        List<SearchAttr> searchAttrList = skuAttrValueList.stream().map((skuAttrValue) -> {
            SearchAttr searchAttr = new SearchAttr();
            searchAttr.setAttrId(skuAttrValue.getBaseAttrInfo().getId());
            searchAttr.setAttrName(skuAttrValue.getBaseAttrInfo().getAttrName());
            searchAttr.setAttrValue(skuAttrValue.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(searchAttrList);
        goodsDao.save(goods);
    }

    //商品下架 删除索引
    @Override
    public void lowerGoods(Long skuId) {
        /*DeleteRequest deleteRequest = new DeleteRequest();
        deleteRequest.index("goods");
        deleteRequest.id(skuId.toString());
        try {
            restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }*/
        goodsDao.deleteById(skuId);
    }

    //商品热度排名 增加热度
    @Override
    public void hotScore(Long skuId) {

        //通过redis进行缓存热度
        Double hotScore = redisTemplate.opsForZSet().incrementScore("hotScore", skuId, 1);

        if (hotScore%10 == 0){
            //查询ES库中的该商品
            Optional<Goods> byId = goodsDao.findById(skuId);
            Goods goods = byId.get();
            //修改热度
            goods.setHotScore(hotScore.longValue());
            //将商品重新存入ES
            goodsDao.save(goods);
        }
    }

    //商品搜索
    @Override
    public SearchResponseVo search(SearchParam searchParam) {
        SearchResponseVo vo = null;
        try {
            //构建搜索请求对象
            SearchRequest searchRequest = buildSearchRequest(searchParam);
            //执行搜索
            SearchResponse searchResponse = restHighLevelClient
                    .search(searchRequest, RequestOptions.DEFAULT);
            //解析结果对象
            vo = parseSearchResponse(searchResponse);
            //当前页
            vo.setPageNo(searchParam.getPageNo());
            //每页数
            vo.setPageSize(searchParam.getPageSize());
            //总页数 (总条数+每页数-1)/每页数
            vo.setTotalPages((vo.getTotal() + vo.getPageSize() - 1)/vo.getPageSize());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return vo;
    }

    private SearchResponseVo parseSearchResponse(SearchResponse searchResponse) {
        SearchResponseVo vo = new SearchResponseVo();

        SearchHits hits = searchResponse.getHits();

        //聚合解析
        Aggregations aggregations = searchResponse.getAggregations();
        Map<String, Aggregation> stringAggregationMap = aggregations.asMap();
        //解析平台属性集合
        Nested aggAttrs = (Nested) stringAggregationMap.get("aggAttrs");
        Terms aggAttrId = aggAttrs.getAggregations().get("aggAttrId");
        List<SearchResponseAttrVo> attrsList = aggAttrId.getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            //平台属性ID
            attrVo.setAttrId(Long.parseLong(bucket.getKeyAsString()));
            //平台属性名称
            Terms aggAttrName = bucket.getAggregations().get("aggAttrName");
            attrVo.setAttrName(aggAttrName.getBuckets().get(0).getKeyAsString());
            //平台属性值
            Terms aggAttrValue = bucket.getAggregations().get("aggAttrValue");
            List<String> attrValueList = aggAttrValue.getBuckets().stream().map(bucket1 -> {
                return bucket1.getKeyAsString();
            }).collect(Collectors.toList());
            attrVo.setAttrValueList(attrValueList);
            return attrVo;
        }).collect(Collectors.toList());
        vo.setAttrsList(attrsList);


        //解析品牌集合
        Terms aggTmId = (Terms) stringAggregationMap.get("aggTmId");
        List<? extends Terms.Bucket> aggTmIdBuckets = aggTmId.getBuckets();
        List<SearchResponseTmVo> searchResponseTmVoList = aggTmIdBuckets.stream().map(bucket -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            //获取品牌ID
            tmVo.setTmId(Long.parseLong(bucket.getKeyAsString()));
            Map<String, Aggregation> aggregationMap = bucket.getAggregations().asMap();
            //获取品牌属性名称
            Terms aggTmName = (Terms) aggregationMap.get("aggTmName");
            String aggTmNameString = aggTmName.getBuckets().get(0).getKeyAsString();
            tmVo.setTmName(aggTmNameString);
            //获取品牌属性图片
            Terms aggTmLogoUrl = (Terms) aggregationMap.get("aggTmLogoUrl");
            String aggTmLogoUrlString = aggTmLogoUrl.getBuckets().get(0).getKeyAsString();
            tmVo.setTmLogoUrl(aggTmLogoUrlString);
            return tmVo;
        }).collect(Collectors.toList());
        vo.setTrademarkList(searchResponseTmVoList);

        //商品结果集
        SearchHit[] hits1 = hits.getHits();
        List<Goods> goodsList = Arrays.stream(hits1).map(hit -> {
            String source = hit.getSourceAsString();
            Goods goods = JSONObject.parseObject(source, Goods.class);
            //高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null && highlightFields.size() >0){
                HighlightField title = highlightFields.get("title");
                if (title != null){
                    Text[] fragments = title.fragments();
                    String titleName = fragments[0].toString();
                    goods.setTitle(titleName);
                }
            }
            return goods;
        }).collect(Collectors.toList());
        vo.setGoodsList(goodsList);

        //总条数
        vo.setTotal(hits.getTotalHits());
        return vo;
    }

    //构建搜索请求对象
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        //创建条件对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)){
            //取交集
            boolQueryBuilder.must(QueryBuilders.matchQuery("title",keyword).operator(Operator.AND));
        } else {
            //搜索全部对象
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        //品牌 过滤条件trademark='+${trademark.tmId}+':'+${trademark.tmName}
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            String[] split = StringUtils.split(trademark, ":");
            boolQueryBuilder.filter(QueryBuilders.termQuery("tmId",split[0]));
        }

        //三级分类
        Long category1Id = searchParam.getCategory1Id();
        if (category1Id != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id",category1Id));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (category2Id != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id",category2Id));
        }
        Long category3Id = searchParam.getCategory3Id();
        if (category3Id != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id",category3Id));
        }

        //平台属性及属性值 '&props='+${baseAttrInfo.attrId}+':'+${attrValue}+':'+${baseAttrInfo.attrName}
        String[] props = searchParam.getProps();
        if (props != null && props.length > 0){
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            for (String prop : props) {
                String[] split = prop.split(":");
                BoolQueryBuilder sonBoolQuery = QueryBuilders.boolQuery();
                sonBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                sonBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
                boolQuery.must(QueryBuilders.nestedQuery("attrs",sonBoolQuery, ScoreMode.None));
            }
            boolQueryBuilder.filter(boolQuery);
        }

        //构建条件对象
        searchSourceBuilder.query(boolQueryBuilder);

        //排序 1:asc 1:desc 2:asc 2:desc
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)){
            String[] split = order.split(":");
            String field = null;
            switch (split[0]){
                case "1": field = "hotScore";
                break;
                case "2": field = "price";
                break;
            }
            searchSourceBuilder.sort(field,split[1].equalsIgnoreCase(SortOrder.ASC.toString())?SortOrder.ASC:SortOrder.DESC);
        }

        //分页
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        searchSourceBuilder.from((pageNo - 1) * pageSize);
        searchSourceBuilder.size(pageSize);

        //品牌聚合
        searchSourceBuilder.aggregation(AggregationBuilders.terms("aggTmId").field("tmId")
        .subAggregation(AggregationBuilders.terms("aggTmName").field("tmName"))
        .subAggregation(AggregationBuilders.terms("aggTmLogoUrl").field("tmLogoUrl")));


        //平台属性聚合
        searchSourceBuilder.aggregation(AggregationBuilders.nested("aggAttrs","attrs")
                .subAggregation(AggregationBuilders.terms("aggAttrId").field("attrs.attrId")
                .subAggregation(AggregationBuilders.terms("aggAttrName").field("attrs.attrName"))
                .subAggregation(AggregationBuilders.terms("aggAttrValue").field("attrs.attrValue"))));

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<font color='red'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);

        SearchRequest searchRequest = new SearchRequest();
        //指定索引库
        searchRequest.indices("goods");
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }
}
